package com.hardson.toolkit.util;

import java.lang.annotation.*;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Maps {

    private Maps() {
    }

    /**
     * 创建一个0长度的哈希映射
     *
     * @param <K> -键类型
     * @param <V> -值类型
     * @return 0长度哈希映射
     */
    public static <K, V> HashMap<K, V> EMPTY_MAP() {
        return new HashMap<>(0);
    }

    /**
     * new HashMap<>()的快捷方法
     *
     * @param <K> -键类型
     * @param <V> -值类型
     * @return 默认创建的哈希映射
     */
    public static <K, V> HashMap<K, V> hashMap() {
        return new HashMap<>();
    }

    /**
     * 将集合列表转化为哈希映射
     *
     * @param list -集合列表
     * @param func -函数式接口提供键的创建方法
     * @param <K>  -键类型
     * @param <V>  -值类型
     * @return 哈希映射
     */
    public static <K, V> Map<K, V> fromList(List<V> list, Function<V, K> func) {
        return list.stream().collect(Collectors.toMap(func, Function.identity()));
    }

    /**
     * 将集合列表转化为哈希映射，key重复时，原key对应的对象被覆盖
     *
     * @param list -集合列表
     * @param func -函数式接口提供键的创建方法
     * @param <K>  -键类型
     * @param <V>  -值类型
     * @return 哈希映射
     */
    public static <K, V> Map<K, V> fromDistinctList(List<V> list, Function<V, K> func) {
        return list.stream()
                .collect(Collectors.toMap(func, Function.identity(), (key1, key2) -> key2));
    }

    /**
     * 将集合列表转化为哈希映射，允许自定义键和值的创建
     *
     * @param list  -集合列表
     * @param func  -函数式接口提供键的创建方法
     * @param func1 -函数式接口提供值的创建方法
     * @param <K>   -键类型
     * @param <V>   -值类型
     * @param <T>   -集合列表值类型
     * @return 哈希映射
     */
    public static <K, V, T> Map<K, V> fromList(List<T> list, Function<T, K> func, Function<T, V> func1) {
        return list.stream().collect(Collectors.toMap(func, func1));
    }

    /**
     * 将集合列表中的元素分组，保存于map对象中。map中的key为分组的键值,一个key对象对应一组对象列表
     *
     * @param <K>  -键类型
     * @param <V>  -集合列表中的对象类型
     * @param list -集合列表
     * @param func -分组处理器函数式接口,将入参元素映射到键
     * @return 哈希映射
     */
    public static <K, V> Map<K, List<V>> groupingBy(List<V> list, Function<V, K> func) {
        return list.stream().collect(Collectors.groupingBy(func));
    }

    /**
     * 根据给定的key从Map对象中获取相应的整数值
     * 如果map为空或获取的对象为空则返回0；如果获取的对象是非int数值型，则转化成int类型；如果获取的对象是String类型则转换成int
     *
     * @param map -Map对象
     * @param key -键
     * @return map对象中获取的整数值
     */
    public static int getIntValue(Map<String, Object> map, String key) {
        if (map == null) {
            return 0;
        }
        Object intVal = map.get(key);
        if (intVal instanceof Number) {
            return ((Number) intVal).intValue();
        }
        return intVal == null ? 0 : Integer.parseInt(intVal.toString());
    }

    /**
     * 根据给定的key从Map对象中获取相应的整数值
     * 如果map为空或获取的对象为空则返回-1；如果获取的对象是非int数值型，则转化成int类型；如果获取的对象是String类型则转换成int
     *
     * @param map Map对象
     * @param key 键
     * @return int map对象中获取的整数值
     */
    public static int getIntMinus1IfNull(Map<String, Object> map, String key) {
        if (map == null) {
            return -1;
        }
        Object intVal = map.get(key);
        if (intVal instanceof Number) {
            return ((Number) intVal).intValue();
        }
        return intVal == null ? -1 : Integer.parseInt(intVal.toString());
    }

    /**
     * 根据给定的key从Map对象中获取相应的字符串值
     * 如果map为空或获取的对象为空则返回空字符串
     *
     * @param map Map对象
     * @param key 键
     * @return map对象中获取的字符串值并进行首尾去空；
     */
    public static String getStrValue(Map<String, Object> map, String key) {
        return (map == null) ? "" : Objects.toString(map.get(key), "").trim();
    }

    /**
     * 将Map对象列表中的Map元素中指定键名的字符串值，以指定分隔符串连起来
     *
     * @param list:      Map对象列表
     * @param keyName:   列表中Map元素的键名
     * @param delimiter: 分隔符
     * @return String 串连的字符串
     * @author hardson
     * @date 2023/9/25 下午5:19
     */
    public static String toJoinedString(List<Map<String, Object>> list, String keyName, String delimiter) {
        if (list == null || list.isEmpty() || keyName == null || (keyName = keyName.trim()).isEmpty()) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (Map<String, Object> obj : list) {
            Object o1 = obj.get(keyName);
            if (sb.length() > 0) {
                sb.append(delimiter);
            }
            sb.append(o1);
        }
        return sb.toString();
    }

    /**
     * 将Map对象列表中的Map元素中指定键名的字符串值，以英文半角逗号串连起来
     *
     * @param list:    Map对象列表
     * @param keyName: 列表中Map元素的键名
     * @return String 串连的字符串
     * @author hardson
     * @date 2023/9/25 下午5:22
     */
    public static String toCommaJoinedString(List<Map<String, Object>> list, String keyName) {
        return toJoinedString(list, keyName, ",");
    }

    /**
     * 将对象中的被MappingAnno注解的成员属性转存到map中。map的key为注解的value值，map的value为被注解的对象属性值
     *
     * @param obj 使用MappingAnno注解属性的java对象
     * @return Map<String, Object>
     * @see MappingAnno
     */
    public static Map<String, Object> toMap(Object obj) {
        Map<String, Object> map = hashMap();
        Stream.of(obj.getClass().getDeclaredFields())
                .parallel()
                .forEach(fld -> {
                    fld.setAccessible(true);
                    if (fld.isAnnotationPresent(MappingAnno.class)) {
                        MappingAnno anno = fld.getAnnotation(MappingAnno.class);
                        String name = anno.value();
                        if (!name.isEmpty()) {
                            try {
                                map.put(name, fld.get(obj));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                });
        return map;
    }

    /**
     * 注解映射对象属性的名称
     *
     * @author hardson
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {ElementType.FIELD, ElementType.TYPE})
    @Documented
    public @interface MappingAnno {
        String value();
    }

    public static void main(String args[]) {
        List<Map<String, Object>> list = new ArrayList<>();
        Map<String, Object> map = new HashMap<>();
        map.put("aaa", "aaa123");
        map.put("bbb", "bbb234");
        list.add(map);
        Map<String, Object> map2 = new HashMap<>();
        map2.put("aaa", "aaa666");
        map2.put("bbb", "bbb777");
        list.add(map2);
        System.out.println(toJoinedString(list, "aaa", "::"));
    }
}
